Форум dkLab и Denwer
Здесь общаются Web-разработчики.
Генеральный спонсор:
Хостинг «Джино»

Функция QSA для URL (Антон Макаренко)
Author Message
Антон Макаренко
Участник форума



Joined: 05 Feb 2004
Posts: 374
Карма: 37
   поощрить/наказать

Location: Киев

PostPosted: Wed Apr 02, 2008 12:36 am (написано за 16 минут 49 секунд)
   Post subject: Функция QSA для URL
Reply with quote

Функция, добавляет/заемещает/удаляет переменные из строки GET-параметров. Нечто похожее на модификатор [QSA] в Apache mod_rewrite, только для PHP.
Покритикуйте, пожалуйста.

$variables
Массив переменных => значений. Логика для каждой переменной такая:
- если значение null, то переменная удаляется из строки
- если пустое, то добавляется/заменяется именем переменной без знака "="
- если со значением, то добавляется/заменяется, а значение экранируется с помощью urlencode()

$query_string
Собственно, строка запроса, в которой проводить изменения. По умолчанию $_SERVER['QUERY_STRING']

$exclude_vars
Массив имен переменных, которых по умолчанию удалять из строки. Задается только если вызвать функцию с $exclude_vars не равным null; при этом запоминается в статическую переменную.
Code (php): скопировать код в буфер обмена
<?php
/**
 * The same as qsarray(), but for one variable only
 *
 * @param string $var_name
 * @param string $var_value
 * @param string $query_string
 * @param array $exclude_vars
 * @return string
 */

function qsa($var_name, $var_value = null, $query_string = null, $exclude_vars = null)
{
    return qsarray(array (www.php.net/array)($var_name => $var_value), $query_string, $exclude_vars);
}

/**
 * Append/replace specified variables in specified query string
 *
 * Possible values for $variables:
 * $variables = array(
 *      'var_1' => 'value_1' // will be added/replaced
 *     ,'var_2' => null      // will be removed
 *     ,'var_3' => ''        // will be added/replaced by empty value
 * )
 *
 * $query_string must be something like $_SERVER['QUERY_STRING']
 *
 * $exclude_vars - array of variable names to be removed by default.
 * If called with not null value, it will be rewritten to static variable
 *
 * @param array $variables
 * @param string $query_string
 * @param array $exclude_vars
 * @return string
 */

function qsarray($variables = array (www.php.net/array)(), $query_string = null, $exclude_vars = null)
{
    // set default query string
    if (null === $query_string)
        $query_string = $_SERVER['QUERY_STRING'];

    // setup variables to be excluded
    static (www.php.net/static) $_exclude_vars_static = array (www.php.net/array)();
    if (null !== $exclude_vars)
        $_exclude_vars_static = $exclude_vars;

    $_exclude_vars = array (www.php.net/array)();
    if (!empty (www.php.net/empty)($_exclude_vars_static))
        foreach ($_exclude_vars_static as $var)
            $_exclude_vars[] = $var;

    // exclude specified variables, too
    if (!empty (www.php.net/empty)($variables))
        foreach ($variables as $var => $value)
            $_exclude_vars[] = $var;

    // parse query and exclude variables
    parse_str (www.php.net/parse_str)($query_string, $parsed_query);
    if (!empty (www.php.net/empty)($_exclude_vars))
        foreach ($_exclude_vars as $var)
            unset (www.php.net/unset)($parsed_query[$var]);

    // add required variables to query
    if (!empty (www.php.net/empty)($variables))
        foreach ($variables as $var => $value)
            if (null !== $value)
                $parsed_query[$var] = $value;

    return http_build_query($parsed_query);
}
.

Пример из жизни
Code (Apache config): скопировать код в буфер обмена
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*?)-(.+)$ index.php?_url=$1&_url_suffix=$2 [NC,L,QSA]
Т.е, в скрипте могут быть переменные $_GET['_url'] и $_GET['_url_suffix']

Функцию используем для построения запросов для header('Location: ...'); в контроллере, а также для "запоминания" некоторых GET-параметров в php-шаблонах.
Чтобы избавиться от ненужных (в ЧПУ) нам _url и _url_suffix, вызываем первый раз лишь чтобы установить дефолты:
Code (php): скопировать код в буфер обмена
qsarray(null, null, array (www.php.net/array)('_url', '_url_suffix'));
.

Для чего это все нужно...

Попробуйте уловить основную мысль, остальное придумаете ;)

При написании админских интерфейсов, бывает, что нужно сортировать таблицы-выборки по одной из колонок с переключением режима ASC/DESC

Мне надоело писать для каждой такой таблицы обработчики снова и снова. Решил унифицировать:
- выборка всегда сортируется по какой-либо колонке (ORDER BY ?d). Значение берется из $_GET['orderby']
- эта колонка сортируется ASC или DESC. ASC по умолчанию, DESC если задан $_GET['desc']
Для этого где-нибудь в начале контроллера (какого-нибудь общего инклуда для группы модулей) это и реализовываем:
Code (php): скопировать код в буфер обмена
// setup cross-script order by and asc/desc parameters
$order_by = 1;
if (!empty (www.php.net/empty)($_GET['orderby']))
    $order_by = @abs (www.php.net/abs)($_GET['orderby']);
define (www.php.net/define)('_ORDER_BY', $order_by); //
define (www.php.net/define)('_ASC_DESC', (isset (www.php.net/isset)($_GET['desc']) ? 'DESC' : 'ASC')); //
define (www.php.net/define)('_IS_DESC', ('ASC' == _ASC_DESC ? null : true)); //
 
Эта унификация сделана ради эксперимента. Как видите, функция qsa() здесь пока что ни при чем.

В контроллере при запросах используем так:
Code (php): скопировать код в буфер обмена
$DB->select('
    SELECT
    ...
    ORDER BY ?d '
. _ASC_DESC .'
    '
, _ORDER_BY
);
Наконец, используем эти константы с qsa() в шаблоне php:
Code (php): скопировать код в буфер обмена
span class="st0">"?<?=qsarray(array('orderby' => _ORDER_BY, 'desc' => _IS_DESC))?>""?<?=qsarray(array('orderby' => 2, 'desc' => ''))?>""?<?=qsarray(array('orderby' => _ORDER_BY, 'desc' => (_IS_DESC ? null : '')))?>">...</a>
Q: Что это нам дает?
A: Возможность делать заголовки колонок с гиперссылками и индикатором сортировки. Т.е. по какой колонке сейчас сортировка, по по возростанию она или нет. При нажатии на ссылку-заголовок без индикатора, делаем сортировку по указанной колонке. А при нажатии на ссылку с индикатором, инвертируем направленность сортировки.
Ничего нового, кроме упрощения такой полезной и такой рутинной фичи качественного пользовательского интерфейса.

См. скриншоты
Code (php): скопировать код в буфер обмена
<?php
$is_desc = (_IS_DESC ? null : '');
$ob = array (www.php.net/array)(
     2 => array (www.php.net/array)('orderby' => 2, 'desc' => $is_desc)
    ,1 => array (www.php.net/array)('orderby' => 1, 'desc' => $is_desc)
    ,3 => array (www.php.net/array)('orderby' => 3, 'desc' => $is_desc)
);
$desc_asc[_ORDER_BY] = (_IS_DESC ? '&#x25B2;' : '&#x25BC;');
?>
<tr>
    <th class="m r"><a href="?<?=tpl_escape(qsarray($ob[2]))?>">ID</a><?=@$desc_asc[2]?></th>
    <th class="m l"><a href="?<?=tpl_escape(qsarray($ob[1]))?>">Товар</a><?=@$desc_asc[1]?></th>
    <th class="m r"><a href="?<?=tpl_escape(qsarray($ob[3]))?>">Цена</a><?=@$desc_asc[3]?></th>
</tr>
Рефакторинг "не отходя от кассы" (красиво, но может показаться непонятным):
Code (php): скопировать код в буфер обмена
<?php
$ob = array (www.php.net/array)();
$not_is_desc = (_IS_DESC ? null : '');
foreach (array (www.php.net/array)(2, 1, 3) as $k)
    $ob[$k] = array (www.php.net/array)('orderby' => $k, 'desc' => ($k == _ORDER_BY ? $not_is_desc : null));
$desc_asc[_ORDER_BY] = (_IS_DESC ? '&#x25B2;' : '&#x25BC;');
?>
<tr>
    <th class="m r"><a href="?<?=tpl_escape(qsarray($ob[2]))?>">ID</a><?=@$desc_asc[2]?></th>
    <th class="m l"><a href="?<?=tpl_escape(qsarray($ob[1]))?>">Товар</a><?=@$desc_asc[1]?></th>
    <th class="m r"><a href="?<?=tpl_escape(qsarray($ob[3]))?>">Цена</a><?=@$desc_asc[3]?></th>
</tr>



1.gif
 Description:
orderby=2
 Filesize:  5.27 KB
 Viewed:  7827 Time(s)

1.gif



2.gif
 Description:
orderby=3&desc
 Filesize:  5.26 KB
 Viewed:  7827 Time(s)

2.gif




Last edited by Антон Макаренко on Wed Apr 02, 2008 10:04 pm; edited 4 times in total
Back to top
View user's profile Send private message Send e-mail
Антон Макаренко
Участник форума



Joined: 05 Feb 2004
Posts: 374
Карма: 37
   поощрить/наказать

Location: Киев

PostPosted: Wed Apr 02, 2008 12:38 am (спустя 1 минуту 34 секунды; написано за 1 минуту 26 секунд)
   Post subject:
Reply with quote

Вот как бы упростить эту кучу регулярных выражений?
[upd] уже неактуально
Code (php): скопировать код в буфер обмена
        $reg_expressions['/&' . $var . '=[^&]+/'] = '';
        $reg_expressions['/&' . $var . '=/'] = '';
        $reg_expressions['/&' . $var . '/'] = '';


Last edited by Антон Макаренко on Wed Apr 02, 2008 10:06 pm; edited 1 time in total
Back to top
View user's profile Send private message Send e-mail
Миша Спларов
Участник форума



Joined: 17 Nov 2003
Posts: 821
Карма: 65
   поощрить/наказать

Location: Россия, Москва

PostPosted: Wed Apr 02, 2008 7:27 am (спустя 6 часов 49 минут; написано за 49 секунд)
   Post subject:
Reply with quote

А не удобней-ли будет использовать массив, который получается после использования фунцкии parse_str?
Back to top
View user's profile Send private message
Антон Макаренко
Участник форума



Joined: 05 Feb 2004
Posts: 374
Карма: 37
   поощрить/наказать

Location: Киев

PostPosted: Wed Apr 02, 2008 10:44 am (спустя 3 часа 16 минут; написано за 41 секунду)
   Post subject:
Reply with quote

Миша Спларов
Хорошая идея, попробую переписать с использованием parse_str() и http_build_query()
Back to top
View user's profile Send private message Send e-mail
Антон Макаренко
Участник форума



Joined: 05 Feb 2004
Posts: 374
Карма: 37
   поощрить/наказать

Location: Киев

PostPosted: Wed Apr 02, 2008 10:09 pm (спустя 11 часов 25 минут; написано за 3 минуты 23 секунды)
   Post subject:
Reply with quote

Обновил функцию в шапке.
Изменения:
  1. (bugfix) переменные, определенные в exclude, удаляются из запроса даже если параметры qsa пустые
  2. рег. выражения заменены стандартными parse_str() и http_build_query()
  3. (feature) в результате поддерживается многомерный массив параметров
Back to top
View user's profile Send private message Send e-mail
Миша Спларов
Участник форума



Joined: 17 Nov 2003
Posts: 821
Карма: 65
   поощрить/наказать

Location: Россия, Москва

PostPosted: Thu Apr 03, 2008 6:18 am (спустя 8 часов 8 минут; написано за 4 минуты 57 секунд)
   Post subject:
Reply with quote

Code (php): скопировать код в буфер обмена
echo (www.php.net/echo) qsarray( array (www.php.net/array)( 'test' => 'val', 'multi' => array (www.php.net/array)( 1, 7 ) ), '?test=10&multi[]=1&multi[]=20&a=100', array (www.php.net/array)( 'a' ) );
Разве это даст на выходе
Code (any language): скопировать код в буфер обмена
?test=val&multi[]=1&multi[]=7&multi[]=20
?
Проверить не могу, т.к. на доступной машине нету php5, но если так, какая поддержка многомерных массивов имеется ввиду? :-)

Кроме этого, какой смысл в двух функциях? Т.е. можно бы было писать только
Code (any language): скопировать код в буфер обмена
qsa( array( 'foo' => 'bar' ) );
- не нужно запоминать два имени и два способа передачи аргументов.
Back to top
View user's profile Send private message
Антон Макаренко
Участник форума



Joined: 05 Feb 2004
Posts: 374
Карма: 37
   поощрить/наказать

Location: Киев

PostPosted: Thu Apr 03, 2008 11:16 am (спустя 4 часа 58 минут; написано за 11 минут 45 секунд)
   Post subject:
Reply with quote

Code (php): скопировать код в буфер обмена
echo (www.php.net/echo) qsarray( array (www.php.net/array)( 'test' => 'val', 'multi' => array (www.php.net/array)( 1, 7 ) ), '?test=10&multi[]=1&multi[]=20&a=100', array (www.php.net/array)( 'a' ) );
выдаст
Code (any language): скопировать код в буфер обмена
%3Ftest=10&test=val&multi%5B0%5D=1&multi%5B1%5D=7
без url-кодирования это выглядит так:
?test=10&test=val&multi[0]=1&multi[1]=7
знак вопроса в начале строки запроса писать не следует (см. $_SERVER['QUERY_STRING']), потому что он "приклеивается" к первому параметру и получается "?test"
если убрать знак вопроса, то выдает:
Code (any language): скопировать код в буфер обмена
test=val&multi%5B0%5D=1&multi%5B1%5D=7
т.е.
test=val&multi[0]=1&multi[1]=7
Насчет двух функций - мне так показалось логичным, если функция позиционируется как аналог QSA в Апаче.
  1. QSA в Апаче использует один аргумент, о массивах речь не идет
  2. qsarray() создана для того, чтоб не вызывать qsa несколько раз, например qsa('var2', 'value2', qsa('var1', 'value1'))
Вобщем, название qsarray() самодостаточно и не противоречит qsa()
Back to top
View user's profile Send private message Send e-mail
Display posts from previous:   
Post new topic   Reply to topic All times are GMT + 3 Hours
Page 1 of 1    Email to a Friend.
You cannot post new topics in this forum. You cannot reply to topics in this forum. You cannot edit your posts in this forum. You cannot delete your posts in this forum. You cannot vote in polls in this forum. You cannot attach files in this forum. You can download files in this forum.
XML